package com.daJiao.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>Title:      RedisConfig. </p>
 * <p>Description redis配置 </p>
 * <p>Copyright: Copyright (c) 2016</p>
 * <p>Company:    医百科技有限公司 </p>
 *
 * @author <a href="xinglinglove1029@163.com"/>杨文生</a>
 * @since 2021/1/7 10:49
 */
@Configuration
@EnableCaching
public class RedisConfig{

  @Value("#{${cache.ttl.config:{}}}")
  private Map<String, Integer> cacheConfig;

  @Resource
  private LettuceConnectionFactory lettuceConnectionFactory;

  @Bean
  public RedisMessageListenerContainer container(LettuceConnectionFactory connectionFactory) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    return container;
  }


  @Bean
  public KeyGenerator keyGenerator() {
    return (target, method, params) -> {
      StringBuilder sb = new StringBuilder();
      sb.append(target.getClass().getName());
      sb.append(method.getName());
      for (Object obj : params) {
        sb.append(obj.toString());
      }
      return sb.toString();
    };

  }

  @Bean
  @ConditionalOnMissingBean(name = "redisTemplate")
  public RedisTemplate<Object, Object> redisTemplate() {
    RedisTemplate<Object, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(lettuceConnectionFactory);
    // 使用Jackson2JsonRedisSerialize 替换默认序列化
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = initJacksonSerializer();
    template.setValueSerializer(jackson2JsonRedisSerializer);
    template.setConnectionFactory(lettuceConnectionFactory);
//        template.setKeySerializer(jackson2JsonRedisSerializer);
    template.setValueSerializer(jackson2JsonRedisSerializer);
    template.setHashKeySerializer(jackson2JsonRedisSerializer);
    template.setHashValueSerializer(jackson2JsonRedisSerializer);
    template.setKeySerializer(new StringRedisSerializer());
    template.afterPropertiesSet();
    return template;
  }


  @Bean
  @ConditionalOnMissingBean(StringRedisTemplate.class)
  public StringRedisTemplate stringRedisTemplate() {
    StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
    stringRedisTemplate.setConnectionFactory(lettuceConnectionFactory);
    return stringRedisTemplate;
  }

  @Bean
  public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    return RedisCacheManager.RedisCacheManagerBuilder
        .fromCacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
        // 默认策略，未配置的 key 会使用这个
        .cacheDefaults(redisConfig(6000))
        // 自定义 key 策略
        .withInitialCacheConfigurations(redisCacheConfigurationMap()).build();
  }


  private Map<String, RedisCacheConfiguration> redisCacheConfigurationMap() {
    Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
    if (cacheConfig != null) {
      cacheConfig.forEach((key, ttl) -> {
        redisCacheConfigurationMap.put(key, redisConfig(ttl));
      });
    }
    return redisCacheConfigurationMap;
  }


  private RedisCacheConfiguration redisConfig(Integer seconds) {
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = initJacksonSerializer();
    RedisSerializationContext.SerializationPair<Object> objectSerializationPair = RedisSerializationContext
        .SerializationPair
        .fromSerializer(jackson2JsonRedisSerializer);

    return RedisCacheConfiguration.defaultCacheConfig()
        .serializeValuesWith(objectSerializationPair)
        // 缓存ttl
        .entryTtl(Duration.ofSeconds(seconds));
  }


  private Jackson2JsonRedisSerializer<Object> initJacksonSerializer() {
    //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值（默认使用JDK的序列化方式）
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    //Jackson2反序列化数据处理LocalDateTime类型时出错
    mapper.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
    mapper.registerModule(new JavaTimeModule());
    jackson2JsonRedisSerializer.setObjectMapper(mapper);
    return jackson2JsonRedisSerializer;
  }
}
